//
//  YHCallStackTracker.swift
//  YHKit
//
//  Created by amovision on 2025/9/13.
//

import Foundation

/// 函数调用信息模型
public struct CallInfo {
    public let fileName: String       // 文件名
    public let lineNumber: Int        // 行号
    public let functionName: String   // 函数名
    public let address: String        // 内存地址
}

/// 调用栈追踪工具库
public class CallStackTracker {
    
    /// 追踪函数调用栈并打印详细信息
    /// - Parameters:
    ///   - functionName: 被追踪的函数名
    ///   - maxDepth: 最大调用栈深度，默认10
    ///   - skipFrames: 需要跳过的栈帧数，默认2（跳过当前函数和track函数本身）
    public static func trackCallStack(for functionName: String,
                                     maxDepth: Int = 10,
                                     skipFrames: Int = 2) {
        let frameCount = Int(maxDepth)
        var callstack = [UnsafeMutableRawPointer?](repeating: nil, count: frameCount)
        let framesCaptured = backtrace(&callstack, Int32(frameCount))
        
        if let symbols = backtrace_symbols(&callstack, framesCaptured) {
            defer { free(symbols) }
            
            var callInfos: [CallInfo] = []
            
            for i in skipFrames..<Int(framesCaptured) {
                if let symbol = symbols[i] {
                    let symbolStr = String(cString: symbol)
                    if let info = parseSymbol(symbolStr) {
                        callInfos.append(info)
                    }
                }
            }
            
            printCallInfo(for: functionName, callInfos: callInfos)
        }
    }
    
    /// 追踪函数调用栈并返回信息（不打印）
    /// - Parameters:
    ///   - maxDepth: 最大调用栈深度
    ///   - skipFrames: 需要跳过的栈帧数
    /// - Returns: 调用信息数组
    public static func getCallStack(maxDepth: Int = 10,
                                   skipFrames: Int = 2) -> [CallInfo] {
        let frameCount = Int(maxDepth)
        var callstack = [UnsafeMutableRawPointer?](repeating: nil, count: frameCount)
        let framesCaptured = backtrace(&callstack, Int32(frameCount))
        
        var callInfos: [CallInfo] = []
        
        if let symbols = backtrace_symbols(&callstack, framesCaptured) {
            defer { free(symbols) }
            
            for i in skipFrames..<Int(framesCaptured) {
                if let symbol = symbols[i] {
                    let symbolStr = String(cString: symbol)
                    if let info = parseSymbol(symbolStr) {
                        callInfos.append(info)
                    }
                }
            }
        }
        
        return callInfos
    }
    
    /// 解析符号信息（内部实现，私有）
    private static func parseSymbol(_ symbol: String) -> CallInfo? {
        // 提取地址
        let addressPattern = #"0x[0-9a-fA-F]+"#
        let address = (symbol.matching(pattern: addressPattern).first ?? "未知地址")
        
        // 提取文件名和行号
        let fileLinePattern = #"\((.*?):(\d+)\)"#
        let fileLineMatches = symbol.matching(pattern: fileLinePattern)
        var fileName = "未知文件"
        var lineNumber = 0
        
        if fileLineMatches.count >= 1 {
            let fileLineStr = fileLineMatches[0]
            let components = fileLineStr.components(separatedBy: ":")
            if components.count >= 2 {
              fileName = components[0].trimmingCharacters(in: CharacterSet(charactersIn: "("))
              lineNumber = Int(components[1].trimmingCharacters(in: CharacterSet(charactersIn: ")"))) ?? 0
            }
        }
        
        // 提取函数名
        let funcPattern = #"-?\[.*?\]|[\w<>]+(\(.*?\))?"#
        let funcName = (symbol.matching(pattern: funcPattern).first ?? "未知函数")
        
        return CallInfo(
            fileName: fileName,
            lineNumber: lineNumber,
            functionName: funcName,
            address: address
        )
    }
    
    /// 打印调用信息（内部实现，私有）
    private static func printCallInfo(for functionName: String, callInfos: [CallInfo]) {
        print("📞 函数调用追踪: \(functionName)")
        print("📚 调用栈信息 (\(callInfos.count)层):")
        print("----------------------------------------")
        
        for (index, info) in callInfos.enumerated() {
            print(String(format: "第%d层调用:", index + 1))
            print("  函数名: \(info.functionName)")
            print("  文件名: \(info.fileName)")
            print("  行号:   \(info.lineNumber)")
            print("  地址:   \(info.address)")
            if index != callInfos.count - 1 {
                print("----------------------------------------")
            }
        }
        
        print("========================================\n")
    }
}

// 辅助扩展（文件内私有，不对外暴露）
fileprivate extension String {
    func matching(pattern: String) -> [String] {
        do {
            let regex = try NSRegularExpression(pattern: pattern)
            let results = regex.matches(in: self, range: NSRange(startIndex..., in: self))
            return results.compactMap {
                Range($0.range, in: self).map { String(self[$0]) }
            }
        } catch {
            return []
        }
    }
}
